
#include <iostream>
#include <stdexcept>

#include <jmlib/unitTester/UnitTesterRegistrator.h>
#include <jmlib/unitTester/asserts.h>

#include <jmlib/cast.h>
#include <jmlib/cmdLineOptions/CmdLineParser.h>


namespace jmtests {
	
	struct  TestY : public jmlib::UnitTest {

		virtual void test01() override {	
			jmlib::CmdLineParser parser;			
			parser.parseCmdLine(0, nullptr);
		}

		virtual void test02() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 1;
			const char* argv [argc] = { "program.exe" };
			parser.parseCmdLine(argc, argv);
		}
		
		virtual void test03() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 2;
			const char* argv [argc] = { "program.exe", "--error_option" };
			JM_EXPECT_EXCEPTION(parser.parseCmdLine(argc, argv), 
				jmlib::CmdLineOptionNotFoundException);
		}

		virtual void test04() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 2;
			const char* argv [argc] = { "program.exe", "--error_option" };
			parser.addOptionDefinition("option_1");
			JM_EXPECT_EXCEPTION(parser.parseCmdLine(argc, argv), 
				jmlib::CmdLineOptionNotFoundException);
		}

		virtual void test05() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 2;
			const char* argv [argc] = { "program.exe", "-o" };
			parser.addOptionDefinition("o");
			parser.addOptionDefinition("x");
			parser.parseCmdLine(argc, argv);
			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["x"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
		}

		virtual void test06() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 2;
			const char* argv [argc] = { "program.exe", "-o" };
			parser.addOptionDefinition("o").addAlias("OO");
			parser.addOptionDefinition("x").addAlias("XX").addAlias("xxx");
			parser.parseCmdLine(argc, argv);
			
			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);
		}

		virtual void test07() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 3;
			const char* argv [argc] = { "program.exe", "--OO", "-x" };
			parser.addOptionDefinition("o").addAlias("OO");
			parser.addOptionDefinition("x").addAlias("XX").addAlias("xxx");
			parser.parseCmdLine(argc, argv);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 0);
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(0), std::out_of_range);
		}

		virtual void test08() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 4;
			const char* argv [argc] = { "program.exe", "--OO", "29", "-x" };
			parser.addOptionDefinition("o").addAlias("OO");
			parser.addOptionDefinition("x").addAlias("XX").addAlias("xxx");
			parser.parseCmdLine(argc, argv);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["o"]->getValues()->size(), 0);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["xxx"]->getValues()->size(), 0);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 1);
			JM_EXPECT_VALUE(parser.getIsolatedArg(0), "29");
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(1), std::out_of_range);
	
		}

		virtual void test09() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 3;
			const char* argv [argc] = { "program.exe", "--OO", "29"};
			parser.addOptionDefinition("o").addAlias("OO");
			parser.addOptionDefinition("x").addAlias("XX").addAlias("xxx");
			parser.parseCmdLine(argc, argv);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["o"]->getValues()->size(), 0);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["xxx"]->getValues()->size(), 0);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 1);
			JM_EXPECT_VALUE(parser.getIsolatedArg(0), "29");
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(1), std::out_of_range);

		}

		virtual void test10() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 4;
			const char* argv [argc] = { "program.exe", "--OO", "29", "-x" };
			parser.addOptionDefinition("o", 
				jmlib::CmdLineOptionDefinition::ONE_OPTIONAL).addAlias("OO");
			parser.addOptionDefinition("x").addAlias("XX").addAlias("xxx");
			parser.parseCmdLine(argc, argv);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["o"]->getValues()->size(), 1);
			JM_EXPECT_VALUE(parser["o"]->getValue<int>(0), 29);
			JM_EXPECT_VALUE(parser["o"]->getValue<std::string>(0), "29");
			JM_EXPECT_EXCEPTION(parser["OO"]->getValue<int>(1), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["xxx"]->getValues()->size(), 0);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 0);
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(0), std::out_of_range);
		}

		virtual void test11() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 5;
			const char* argv [argc] = 
				{ "program.exe", "--OO", "29", "22", "-x" };
			
			parser.addOptionDefinition("o", 
				jmlib::CmdLineOptionDefinition::ONE_OPTIONAL).addAlias("OO");

			parser.addOptionDefinition("x", 
				jmlib::CmdLineOptionDefinition::ONE_REQUIRED)
					.addAlias("XX").addAlias("xxx");

			JM_EXPECT_EXCEPTION(parser.parseCmdLine(argc, argv),
				jmlib::CmdLineOptionMissingValueException);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["o"]->getValues()->size(), 1);
			JM_EXPECT_VALUE(parser["o"]->getValue<int>(0), 29);
			JM_EXPECT_VALUE(parser["o"]->getValue<std::string>(0), "29");
			JM_EXPECT_EXCEPTION(parser["OO"]->getValue<int>(1), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["xxx"]->getValues()->size(), 0);
			JM_EXPECT_EXCEPTION(parser["XX"]->getValue<std::string>(1), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 1);
			JM_EXPECT_VALUE(parser.getIsolatedArg(0), "22");
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(1), std::out_of_range);
		}

		virtual void test12() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 7;
			const char* argv [argc] = 
			{ "program.exe", "--OO", "29", "22", "yz", "-x", "0.9" };

			parser.addOptionDefinition("o", 
				jmlib::CmdLineOptionDefinition::ONE_OPTIONAL).addAlias("OO");

			parser.addOptionDefinition("x", 
				jmlib::CmdLineOptionDefinition::ONE_REQUIRED)
				.addAlias("XX").addAlias("xxx");

			parser.parseCmdLine(argc, argv);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["o"]->getValues()->size(), 1);
			JM_EXPECT_VALUE(parser["o"]->getValue<int>(0), 29);
			JM_EXPECT_VALUE(parser["o"]->getValue<std::string>(0), "29");
			JM_EXPECT_EXCEPTION(parser["OO"]->getValue<int>(1), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["xxx"]->getValues()->size(), 1);

			JM_EXPECT_VALUE(parser["x"]->getValue<double>(0), 0.9);
			JM_EXPECT_VALUE(parser["x"]->getValue<float>(0), 0.9f);
			JM_EXPECT_VALUE(parser["x"]->getValue<std::string>(0), "0.9");
			JM_EXPECT_EXCEPTION(parser["XX"]->getValue<std::string>(2), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 2);
			JM_EXPECT_VALUE(parser.getIsolatedArg(0), "22");
			JM_EXPECT_VALUE(parser.getIsolatedArg(1), "yz");
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(2), std::out_of_range);
		}

		virtual void test13() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 6;
			const char* argv [argc] = 
			{ "program.exe", "arg", "--OO", "-x", "-v", "lll" };

			parser.addOptionDefinition("o", 
				jmlib::CmdLineOptionDefinition::MULTI_OPTIONAL).addAlias("OO");

			parser.addOptionDefinition("x", 
				jmlib::CmdLineOptionDefinition::MULTI_OPTIONAL)
				.addAlias("XX").addAlias("xxx");

			parser.addOptionDefinition("v");
			parser.addOptionDefinition("q");

			parser.parseCmdLine(argc, argv);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["o"]->getValues()->size(), 0);
			JM_EXPECT_EXCEPTION(parser["OO"]->getValue<int>(0), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["xxx"]->getValues()->size(), 0);
			JM_EXPECT_EXCEPTION(parser["XX"]->getValue<std::string>(0), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser["v"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["V"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["v"]->getValues()->size(), 0);
			JM_EXPECT_EXCEPTION(parser["v"]->getValue<std::string>(0), 
				std::out_of_range);


			JM_EXPECT_VALUE(parser["q"]->isUsed(), false);
			JM_EXPECT_EXCEPTION(parser["Q"], 
				jmlib::CmdLineOptionNotFoundException);

				JM_EXPECT_VALUE(parser["q"]->getValues()->size(), 0);
			JM_EXPECT_EXCEPTION(parser["q"]->getValue<std::string>(0), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 2);
			JM_EXPECT_VALUE(parser.getIsolatedArg(0), "arg");
			JM_EXPECT_VALUE(parser.getIsolatedArg(1), "lll");
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(2), std::out_of_range);
		}
		
		virtual void test14() override {	
			jmlib::CmdLineParser parser;			
			const int argc = 7;
			const char* argv [argc] = 
			{ "program.exe", "--OO", "29", "22", "yz", "-x", "0.9" };

			parser.addOptionDefinition("o", 
				jmlib::CmdLineOptionDefinition::MULTI_OPTIONAL).addAlias("OO");

			parser.addOptionDefinition("x", 
				jmlib::CmdLineOptionDefinition::MULTI_OPTIONAL)
				.addAlias("XX").addAlias("xxx");

			parser.parseCmdLine(argc, argv);

			JM_EXPECT_VALUE(parser["o"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["O"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["OO"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["oo"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["o"]->getValues()->size(), 3);
			JM_EXPECT_VALUE(parser["o"]->getValue<int>(0), 29);
			JM_EXPECT_VALUE(parser["o"]->getValue<std::string>(0), "29");
			JM_EXPECT_VALUE(parser["o"]->getValue<int>(1), 22);
			JM_EXPECT_VALUE(parser["o"]->getValue<std::string>(1), "22");
			JM_EXPECT_VALUE(parser["o"]->getValue<std::string>(2), "yz");
			JM_EXPECT_EXCEPTION(parser["OO"]->getValue<int>(3), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser["x"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["X"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["XX"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["xx"], 
				jmlib::CmdLineOptionNotFoundException);
			JM_EXPECT_VALUE(parser["xxx"]->isUsed(), true);
			JM_EXPECT_EXCEPTION(parser["XXX"], 
				jmlib::CmdLineOptionNotFoundException);

			JM_EXPECT_VALUE(parser["xxx"]->getValues()->size(), 1);

			JM_EXPECT_VALUE(parser["x"]->getValue<double>(0), 0.9);
			JM_EXPECT_VALUE(parser["x"]->getValue<float>(0), 0.9f);
			JM_EXPECT_VALUE(parser["x"]->getValue<std::string>(0), "0.9");
			JM_EXPECT_EXCEPTION(parser["XX"]->getValue<std::string>(2), 
				std::out_of_range);

			JM_EXPECT_VALUE(parser.isolatedArgsCount(), 0);
			JM_EXPECT_EXCEPTION(parser.getIsolatedArg(0), std::out_of_range);
		}
	};

	static jmlib::UnitTesterRegistrator r(new TestY());

}
